/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package jmmos.gameserver.model.actor;

import jmmos.gameserver.ai.CtrlEvent;
import jmmos.gameserver.model.CharEffectList;
import jmmos.gameserver.model.actor.instance.MMOPcInstance;
import jmmos.gameserver.model.actor.knownlist.PlayableKnownList;
import jmmos.gameserver.model.actor.stat.PlayableStat;
import jmmos.gameserver.model.actor.status.PlayableStatus;
import jmmos.gameserver.model.actor.templates.MMOCharTemplate;
import jmmos.gameserver.model.effects.MMOEffect;
import jmmos.gameserver.model.effects.MMOEffectType;
import jmmos.gameserver.model.quest.QuestState;
import jmmos.gameserver.model.skills.MMOSkill;

/**
 * This class represents all Playable characters in the world.<br>
 * MMOPlayable:
 * <ul>
 * <li>MMOPcInstance</li>
 * <li>MMOSummon</li>
 * </ul>
 */
public abstract class MMOPlayable extends MMOCharacter
{
	private MMOCharacter _lockedTarget = null;
	
	/**
	 * Constructor of MMOPlayable.<br>
	 * <B><U> Actions</U> :</B>
	 * <ul>
	 * <li>Call the MMOCharacter constructor to create an empty _skills slot and link copy basic Calculator set to this MMOPlayable</li>
	 * </ul>
	 * @param objectId Identifier of the object to initialized
	 * @param template The MMOCharTemplate to apply to the MMOPlayable
	 */
	public MMOPlayable(int objectId, MMOCharTemplate template)
	{
		super(objectId, template);
		setInstanceType(InstanceType.MMOPlayable);
		setIsInvul(false);
	}
	
	@Override
	public PlayableKnownList getKnownList()
	{
		return (PlayableKnownList) super.getKnownList();
	}
	
	@Override
	public void initKnownList()
	{
		setKnownList(new PlayableKnownList(this));
	}
	
	@Override
	public PlayableStat getStat()
	{
		return (PlayableStat) super.getStat();
	}
	
	@Override
	public void initCharStat()
	{
		setStat(new PlayableStat(this));
	}
	
	@Override
	public PlayableStatus getStatus()
	{
		return (PlayableStatus) super.getStatus();
	}
	
	@Override
	public void initCharStatus()
	{
		setStatus(new PlayableStatus(this));
	}
	
	@Override
	public boolean doDie(MMOCharacter killer)
	{
		// killing is only possible one time
		synchronized (this)
		{
			if (isDead())
			{
				return false;
			}
			// now reset currentHp to zero
			setCurrentHp(0);
			setIsDead(true);
		}
		
		/**
		 * This needs to stay here because it overrides MMOCharacter.doDie() and does not call for super.doDie()
		 */
		if (!super.fireDeathListeners(killer))
		{
			return false;
		}
		
		// Set target to null and cancel Attack or Cast
		setTarget(null);
		
		// Stop movement
		stopMove(null);
		
		// Stop HP/MP/CP Regeneration task
		getStatus().stopHpMpRegeneration();
		
		// Stop all active skills effects in progress on the MMOCharacter,
		// if the Character isn't affected by Soul of The Phoenix or Salvation
		if (isPhoenixBlessed())
		{
			if (getCharmOfLuck())
			{
				stopCharmOfLuck(null);
			}
			if (isNoblesseBlessed())
			{
				stopNoblesseBlessing(null);
			}
		}
		// Same thing if the Character isn't a Noblesse Blessed MMOPlayable
		else if (isNoblesseBlessed())
		{
			stopNoblesseBlessing(null);
			
			if (getCharmOfLuck())
			{
				stopCharmOfLuck(null);
			}
		}
		else
		{
			stopAllEffectsExceptThoseThatLastThroughDeath();
		}
		
		// Send the Server->Client packet StatusUpdate with current HP and MP to all other MMOPcInstance to inform
		broadcastStatusUpdate();
		
		if (getWorldRegion() != null)
		{
			getWorldRegion().onDeath(this);
		}
		
		// Notify Quest of MMOPlayable's death
		MMOPcInstance actingPlayer = getActingPlayer();
		if (!actingPlayer.isNotifyQuestOfDeathEmpty())
		{
			for (QuestState qs : actingPlayer.getNotifyQuestOfDeath())
			{
				qs.getQuest().notifyDeath((killer == null ? this : killer), this, qs);
			}
		}
		
		if (killer != null)
		{
			MMOPcInstance player = killer.getActingPlayer();
			
			if (player != null)
			{
				player.onKillUpdatePvPKarma(this);
			}
		}
		
		// Notify MMOCharacter AI
		getAI().notifyEvent(CtrlEvent.EVT_DEAD);
		
		return true;
	}
	
	public boolean checkIfPvP(MMOCharacter target)
	{
		if (target == null)
		{
			return false; // Target is null
		}
		if (target == this)
		{
			return false; // Target is self
		}
		if (!target.isPlayable())
		{
			return false; // Target is not a MMOPlayable
		}
		
		final MMOPcInstance player = getActingPlayer();
		if (player == null)
		{
			return false; // Active player is null
		}
		
		if (player.getKarma() != 0)
		{
			return false; // Active player has karma
		}
		
		final MMOPcInstance targetPlayer = target.getActingPlayer();
		if (targetPlayer == null)
		{
			return false; // Target player is null
		}
		
		if (targetPlayer == this)
		{
			return false; // Target player is self
		}
		if (targetPlayer.getKarma() != 0)
		{
			return false; // Target player has karma
		}
		if (targetPlayer.getPvpFlag() == 0)
		{
			return false;
		}
		
		return true;
		// Even at war, there should be PvP flag
		// if(
		// player.getGuild() == null ||
		// targetPlayer.getGuild() == null ||
		// (
		// !targetPlayer.getGuild().isAtWarWith(player.getGuildId()) &&
		// targetPlayer.getWantsPeace() == 0 &&
		// player.getWantsPeace() == 0
		// )
		// )
		// {
		// return true;
		// }
		// return false;
	}
	
	/**
	 * Return True.
	 */
	@Override
	public boolean isAttackable()
	{
		return true;
	}
	
	// Support for Noblesse Blessing skill, where buffs are retained
	// after resurrect
	public final boolean isNoblesseBlessed()
	{
		return _effects.isAffected(CharEffectList.EFFECT_FLAG_NOBLESS_BLESSING);
	}
	
	public final void stopNoblesseBlessing(MMOEffect effect)
	{
		if (effect == null)
		{
			stopEffects(MMOEffectType.NOBLESSE_BLESSING);
		}
		else
		{
			removeEffect(effect);
		}
		updateAbnormalEffect();
	}
	
	// Support for Soul of the Phoenix and Salvation skills
	public final boolean isPhoenixBlessed()
	{
		return _effects.isAffected(CharEffectList.EFFECT_FLAG_PHOENIX_BLESSING);
	}
	
	public final void stopPhoenixBlessing(MMOEffect effect)
	{
		if (effect == null)
		{
			stopEffects(MMOEffectType.PHOENIX_BLESSING);
		}
		else
		{
			removeEffect(effect);
		}
		
		updateAbnormalEffect();
	}
	
	/**
	 * @return True if the Silent Moving mode is active.
	 */
	public boolean isSilentMoving()
	{
		return _effects.isAffected(CharEffectList.EFFECT_FLAG_SILENT_MOVE);
	}
	
	// for Newbie Protection Blessing skill, keeps you safe from an attack by a chaotic character >= 10 levels apart from you
	public final boolean getProtectionBlessing()
	{
		return _effects.isAffected(CharEffectList.EFFECT_FLAG_PROTECTION_BLESSING);
	}
	
	/**
	 * @param effect
	 */
	public void stopProtectionBlessing(MMOEffect effect)
	{
		if (effect == null)
		{
			stopEffects(MMOEffectType.PROTECTION_BLESSING);
		}
		else
		{
			removeEffect(effect);
		}
		
		updateAbnormalEffect();
	}
	
	// Charm of Luck - During a Raid/Boss war, decreased chance for death penalty
	public final boolean getCharmOfLuck()
	{
		return _effects.isAffected(CharEffectList.EFFECT_FLAG_CHARM_OF_LUCK);
	}
	
	public final void stopCharmOfLuck(MMOEffect effect)
	{
		if (effect == null)
		{
			stopEffects(MMOEffectType.CHARM_OF_LUCK);
		}
		else
		{
			removeEffect(effect);
		}
		
		updateAbnormalEffect();
	}
	
	@Override
	public void updateEffectIcons(boolean partyOnly)
	{
		_effects.updateEffectIcons(partyOnly);
	}
	
	public boolean isLockedTarget()
	{
		return _lockedTarget != null;
	}
	
	public MMOCharacter getLockedTarget()
	{
		return _lockedTarget;
	}
	
	public void setLockedTarget(MMOCharacter cha)
	{
		_lockedTarget = cha;
	}
	
	MMOPcInstance transferDmgTo;
	
	public void setTransferDamageTo(MMOPcInstance val)
	{
		transferDmgTo = val;
	}
	
	public MMOPcInstance getTransferingDamageTo()
	{
		return transferDmgTo;
	}
	
	public abstract int getKarma();
	
	public abstract byte getPvpFlag();
	
	public abstract boolean useMagic(MMOSkill skill, boolean forceUse, boolean dontMove);
	
	public abstract void store();
	
	public abstract void storeEffect(boolean storeEffects);
	
	public abstract void restoreEffects();
	
	@Override
	public boolean isPlayable()
	{
		return true;
	}
}
